{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0c324802",
   "metadata": {},
   "source": [
    "# python基础入门4_函数\n",
    "\n",
    "在Python编程语言中，函数是一种组织好的、可重复使用的代码段，用于实现单一或相关联的功能。你可以自定义一个符合自己需求的函数，其基本规则如下：\n",
    "\n",
    "函数代码块以 def 关键词开头，后接函数标识符名称和圆括号 ()。\n",
    "\n",
    "任何传入参数和自变量必须放在圆括号中间，圆括号之间可以用于定义参数。\n",
    "\n",
    "函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。\n",
    "\n",
    "函数内容以冒号 :起始，并且缩进。\n",
    "\n",
    "此外，Python还支持函数递归，这意味着定义的函数能够调用自身。递归是一种常见的数学和编程概念，它通过循环访问数据以达成结果。同时，Python中定义好的函数是可以通过函数名称调取并重复使用的模块。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "34d7bcbf",
   "metadata": {},
   "source": [
    "## 函数定义与调用\n",
    "\n",
    "### 函数定义\n",
    "\n",
    "在Python编程语言中，函数的定义包括函数标识符名称、圆括号 ()、文档字符串和冒号 :。具体来说，函数代码块以 def 关键词开头，后接函数标识符名称和圆括号 ()。紧接着，你可以在第一行语句选择性地使用文档字符串—用于存放函数说明。函数内容以冒号 :起始，并且需要进行适当的缩进。\n",
    "\n",
    "补充知识：文档字符串（docstring）是用于存放函数说明的字符串，通常位于函数的第一行。它的作用是解释函数的功能、参数和返回值等信息，方便其他程序员理解和使用该函数。文档字符串可以使用三个双引号（\"\"\"）或三个单引号（'''）括起来，也可以使用三对连续的单引号或双引号。在文档字符串中，可以使用Markdown语法来格式化文本，例如加粗、斜体、链接等。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "99de5dd5",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add(a , b):\n",
    "    \"\"\"\n",
    "    计算两个数的和并返回结果。\n",
    "\n",
    "    参数：\n",
    "    a -- 第一个加数\n",
    "    b -- 第二个加数\n",
    "\n",
    "    返回值：\n",
    "    int -- 两个数的和\n",
    "    \"\"\"\n",
    "    return a + b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "f2616dbc",
   "metadata": {},
   "outputs": [],
   "source": [
    "def say_hello():\n",
    "    \"\"\"\n",
    "    打印输出:hello\n",
    "    \"\"\"\n",
    "    print(\"hello\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e7d1c40",
   "metadata": {},
   "source": [
    "### 函数调用\n",
    "\n",
    "在Python编程语言中，函数调用是指在程序的某处调用已定义的函数。你可以通过函数名和圆括号的方式完成函数调用，其中圆括号内可以包含实际参数。例如，如果你定义了一个名为add的函数，其功能是计算两个数的和，那么可以通过add(3, 4)这样的方式调用这个函数并打印结果。\n",
    "\n",
    "此外，你还可以通过以下方式进行函数调用：\n",
    "\n",
    "直接从Python提示符执行函数，例如输入函数名和实际参数即可。\n",
    "\n",
    "在另一个函数中调用一个函数，也就是嵌套函数。\n",
    "\n",
    "使用return语句从函数中返回一个值，一旦函数被调用，return语句可以包含一个要执行的表达式。\n",
    "\n",
    "值得注意的是，Python支持多种类型的函数，包括普通函数、匿名函数（也称为Lambda函数）、递归函数和高阶函数等。不同类型的函数在具体应用和调用方式上可能会有所差异。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "87c8046e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(3,4)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "2f07293b",
   "metadata": {},
   "outputs": [],
   "source": [
    "res = add(1,1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "63f9434b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "2"
      ]
     },
     "execution_count": 15,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "res"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "0263b59a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "hello\n"
     ]
    }
   ],
   "source": [
    "say_hello()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "00492891",
   "metadata": {},
   "source": [
    "## 函数的不同种类的参数\n",
    "\n",
    "在Python编程语言中，函数的参数是一种定义在函数名后的括号内，可以传递到函数中的值。你可以使用不同类型的参数来定制你的函数行为。\n",
    "\n",
    "Python支持五种类型的参数：必选参数、默认参数、可变参数、关键字参数和命名关键字参数。\n",
    "\n",
    "必选参数：这是函数定义时必须要有的参数，调用函数时必须传入对应的实际参数。\n",
    "\n",
    "默认参数：这种参数在函数定义时不必传入，但在函数调用时如果没有提供对应的实际参数，那么就会使用默认值。注意，如果有多个默认参数，它们必须\n",
    "在非默认参数之后。\n",
    "\n",
    "可变参数：这种参数可以接受任意数量的实际参数。在定义时，可变参数前面有一个星号(*)。\n",
    "\n",
    "关键字参数：这种参数允许你通过参数名指定实际参数的值，而不是通过位置。在函数调用时，关键字参数的顺序必须是匹配的。\n",
    "\n",
    "命名关键字参数：这种参数结合了关键字参数和可变参数的特性。它可以接收任意数量的实际参数，并且可以通过参数名指定实际参数的值。在函数调用时，命名关键字参数的顺序是无所谓的。\n",
    "\n",
    "这些参数类型可以组合使用，但是请注意，参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dc1c960f",
   "metadata": {},
   "source": [
    "### 必选参数（位置参数）\n",
    "\n",
    "在Python编程语言中，必选参数是函数定义时必须要有的参数，调用函数时必须传入对应的实际参数。这意味着调用时的数量必须和声明时的一样，且参数位置参数须以正确的顺序传入函数。例如，我们有一个名为power的函数，该函数包含两个必选参数x和n，当调用这个函数时，需要传入两个值按照位置顺序依次赋给参数x和n，如power(3, 4)。\n",
    "\n",
    "此外，需要注意的是，参数定义的顺序必须是：必选参数、默认参数、可变参数、命名关键字参数和关键字参数。如果违反这个顺序，Python解释器将无法正确解析参数。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "id": "d2fc0a0d",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "7"
      ]
     },
     "execution_count": 30,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add(3,4)#带有必选参数"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 61,
   "id": "c8c70904",
   "metadata": {},
   "outputs": [],
   "source": [
    "def power_(x,n):\n",
    "    q=x\n",
    "    for i in range(1,n):\n",
    "        x=q*x\n",
    "    return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 62,
   "id": "f6ee0dee",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 62,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power_(2,2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 63,
   "id": "e0a8ac74",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "8"
      ]
     },
     "execution_count": 63,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power_(2,3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 64,
   "id": "ea0749f8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "125"
      ]
     },
     "execution_count": 64,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power_(5,3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 65,
   "id": "c00d8457",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "power_() missing 1 required positional argument: 'n'",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[1;32m~\\AppData\\Local\\Temp/ipykernel_17988/3982824517.py\u001b[0m in \u001b[0;36m<module>\u001b[1;34m\u001b[0m\n\u001b[1;32m----> 1\u001b[1;33m \u001b[0mpower_\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;36m4\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[1;31mTypeError\u001b[0m: power_() missing 1 required positional argument: 'n'"
     ]
    }
   ],
   "source": [
    "power_(4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d4f19004",
   "metadata": {},
   "source": [
    "### 默认参数\n",
    "\n",
    "在Python编程语言中，默认参数是函数定义时不必传入的参数，但在函数调用时如果没有提供对应的实际参数，那么就会使用默认值。定义默认参数的方式是在函数定义时直接给参数赋值，且赋值语句要放在函数参数列表中非默认参数后面。\n",
    "\n",
    "例如，我们有一个名为power的函数，该函数包含两个必选参数x和n，以及一个默认参数y。当调用这个函数时，可以只传入前两个必选参数x和n的实际参数，如power(3, 4)；也可以传入所有三个参数的实际参数，如power(3, 4, 2)；还可以只传入后一个默认参数y的实际参数，如power(3, 4, y=2)。\n",
    "\n",
    "需要注意的是，如果有多个默认参数，它们必须在非默认参数之后。此外，带默认值的参数应该放在函数参数列表的末尾。这是因为Python解释器在解析函数调用时会从左到右匹配参数，如果遇到默认值就停止匹配并使用默认值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 57,
   "id": "4961be09",
   "metadata": {},
   "outputs": [],
   "source": [
    "def power(x,n=2):\n",
    "    q=x\n",
    "    for i in range(1,n):\n",
    "        x=q*x\n",
    "    return x"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 66,
   "id": "ab42ad2c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 66,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 67,
   "id": "865b3bf4",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "9"
      ]
     },
     "execution_count": 67,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(3)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 68,
   "id": "2dd7eef9",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "81"
      ]
     },
     "execution_count": 68,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "power(3,4)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e811a20d",
   "metadata": {},
   "source": [
    "### 可变参数\n",
    "\n",
    "在Python编程语言中，可变参数也被称为任意参数，意味着函数可以接受任意数量的参数，而不仅仅是固定的位置参数或关键字参数。\n",
    "\n",
    "这种参数在函数定义时使用**args和kwargs语法来标识。其中，*args用于收集所有位置参数到一个元组中，而kwargs则用于收集所有关键字参数到一个字典中。\n",
    "\n",
    "例如，我们定义一个函数add_numbers，该函数可以接受任意数量的位置参数和关键字参数：\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "32761e8a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_numbers(*args, **kwargs):\n",
    "    total = 0\n",
    "    for arg in args:\n",
    "        total += arg\n",
    "    for key, value in kwargs.items():\n",
    "        total += value\n",
    "    return total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 74,
   "id": "c9f76d72",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 74,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_numbers(1, 2, 3, a=4, b=5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 85,
   "id": "54e65766",
   "metadata": {},
   "outputs": [],
   "source": [
    "def add_numbers(*args, **kwargs):\n",
    "    print(args)#打印元组\n",
    "    print(kwargs)#打印字典\n",
    "    total = 0\n",
    "    for arg in args:\n",
    "        total += arg\n",
    "    for key, value in kwargs.items():\n",
    "        total += value\n",
    "    return total"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 86,
   "id": "d31733ca",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3, 4, 5)\n",
      "{}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 86,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_numbers(1, 2, 3, 4, 5)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 87,
   "id": "32860cae",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "(1, 2, 3)\n",
      "{'a': 4, 'b': 5}\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "15"
      ]
     },
     "execution_count": 87,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "add_numbers(1, 2, 3, a=4, b=5)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ac64c7a1",
   "metadata": {},
   "source": [
    "### 关键字参数\n",
    "\n",
    "在Python编程语言中，关键字参数是传递给函数的一种参数类型。这种参数在调用函数时，使用“参数名=参数值”的形式指定具体的值。例如，我们有一个名为get_net_price的函数，该函数接受两个关键字参数：price和discoun。当我们调用这个函数时，需要传入实际的参数值，如get_net_price(price=100, discoun=0.2)。\n",
    "\n",
    "此外，需要注意的是，关键字参数可以跟位置参数（必要参数）混合使用，但位置参数必须位于关键字参数之前。同时，关键字参数也可以有默认值，如果调用时没有提供对应的实际参数，那么就会使用默认值。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "1e579b88",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "80.0\n"
     ]
    }
   ],
   "source": [
    "def get_net_price(price=100, discount=0.2):\n",
    "    return price * (1 - discount)\n",
    "\n",
    "result = get_net_price(price=100, discount=0.2)\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "125f24e1",
   "metadata": {},
   "source": [
    "### 命名关键字参数\n",
    "\n",
    "在Python中，命名关键字参数是一种功能，允许你在调用函数时使用特定的键值对作为参数。这在处理大量数据或配置选项时特别有用，因为它可以使代码更具可读性和灵活性。\n",
    "\n",
    "\n",
    "基础用法：当你有一个字典对象，且其中的键都是合法的形参名时，你可以通过给这些键加个前缀****来将它们作为关键字参数传入函数。例如，如果你有两个字典dct1和dct2，你可以这样调用函数：func(******dct1, **dct2)。\n",
    "\n",
    "\n",
    "仅关键字参数：在后面定义的参数（或在args后面定义）是仅关键字参数。这意味着在调用函数时，必需明确指定变量名。例如，在函数定义中，你可以这样写：def func(a, *, b): pass。这里，a是一个位置参数，而b是一个仅关键字参数。\n",
    "\n",
    "\n",
    "集合关键字参数：这是通过**args指定的参数，它可以接受任意数量的关键字参数。与仅关键字参数不同，集合关键字参数在传参时不需要带变量名。\n",
    "\n",
    "\n",
    "错误处理：如果调用函数缺少了位置参数，Python会报错。在这种情况下，默认参数就会派上用场。例如，你可以在函数定义中为第二个参数设置默认值：def power(x, n=2): ...。这样，当你调用该函数并只传入一个参数时，它将使用默认值n=2。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 100,
   "id": "6bbebb2a",
   "metadata": {},
   "outputs": [],
   "source": [
    "def greet(name, greeting=\"你好\"):\n",
    "    return f\"{greeting}, {name}!\"\n",
    "#在Python中，f是一个前缀，表示字符串格式化。当你在字符串前面加上f或F时，可以在里面使用大括号 {} 来包裹变量名，\n",
    "#从而实现字符串的插值。在这个例子中，f\"{greeting}, {name}!\"表示将greeting和name变量的值插入到字符串中，生成一个新的字符串。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 101,
   "id": "78e847f6",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "你好, 张三!\n",
      "欢迎, 李四!\n"
     ]
    }
   ],
   "source": [
    "print(greet(\"张三\"))  # 输出：你好， 张三！\n",
    "print(greet(\"李四\", greeting=\"欢迎\"))  # 输出：欢迎， 李四！"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c863f0df",
   "metadata": {},
   "source": [
    "在这个例子中，我们定义了一个名为greet的函数，它接受两个参数：name和greeting。其中，greeting是一个命名关键字参数，它的默认值为\"你好\"。当我们调用这个函数时，可以通过传递greeting参数来指定不同的问候语。"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "64d3bff1",
   "metadata": {},
   "source": [
    "## 函数的递归调用\n",
    "\n",
    "函数递归调用是指在一个函数内部，直接或间接地调用该函数本身。递归调用通常用于解决分治问题、阶乘、斐波那契数列等需要重复执行相同操作的问题。\n",
    "\n",
    "递归调用的基本结构如下：\n",
    "\n",
    "定义基本情况：当满足某个条件时，函数不再进行递归调用，而是直接返回结果。\n",
    "\n",
    "定义递归情况：当不满足基本情况时，函数继续调用自身，但每次调用时都会将问题规模缩小，直到达到基本情况。\n",
    "\n",
    "以下是一个计算阶乘的递归函数示例："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 103,
   "id": "b7197ead",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "120\n"
     ]
    }
   ],
   "source": [
    "def factorial(n):\n",
    "    # 基本情况：当n为1时，返回1\n",
    "    if n == 1:\n",
    "        return 1\n",
    "    # 递归情况：当n大于1时，返回n乘以(n-1)的阶乘\n",
    "    else:\n",
    "        return n * factorial(n - 1)\n",
    "\n",
    "# 测试\n",
    "print(factorial(5))  # 输出：120"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3c84d697",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.9.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
